import random

from animal_chess import LENGTH, WIDTH, CHESS_COMBINATIONS, ChessError, ChessType, ChessColor


class Chess:
    '棋子，包含类型，颜色，是否隐藏，位置（X坐标，Y坐标）属性'

    def __init__(self, type=None, color=None, hidden=True, x=None, y=None) -> None:
        self.type = type
        self.color = color
        self.hidden = hidden
        self.x = x
        self.y = y

    def __str__(self) -> str:
        return '{type: %s, color: %s, hidden: %s, position: {x: %s, y: %s}}' % (
            self.type, self.color, self.hidden, self.x, self.y)


class ChessBoard:
    '棋盘'

    def __init__(self, length=LENGTH, width=WIDTH, chess_combinations=CHESS_COMBINATIONS) -> None:
        if type(length) != int or type(width) != int or length <= 0 or width <= 0:
            raise ChessError('棋盘长宽设置必须为正整数')

        if (length * width) % 2 != 0:
            raise ChessError('棋盘棋子个数不能为奇数，对战双方无法平均分配棋子')

        if sum(chess_combinations.values()) != (length * width) // 2:
            raise ChessError('棋盘棋子组合必须为%s个，设置CHESS_COMBINATIONS' % ((length * width) // 2))

        self.length = length
        self.width = width
        # 默认生成一张空的棋盘
        self.chesses = [[None for y in range(self.length)] for x in range(self.width)]

        # 为空棋盘的每个位置随机分配一个棋子
        tmp_chesses = []
        for key, value in chess_combinations.items():
            for i in range(0, value):
                for color in ChessColor:
                    tmp_chesses.append(Chess(type=key, color=color))

        tmp_chesses = random.sample(tmp_chesses, self.length * self.width)
        for index, value in enumerate(tmp_chesses):
            x, y = index // self.length, index % self.length
            chess = tmp_chesses[index]
            chess.x, chess.y = x, y
            self.chesses[x][y] = chess

    def __can_eat(self, src_chess, des_chess):
        '判断当前棋子能否吃掉目标棋子'
        if src_chess is None or des_chess is None or src_chess.hidden or des_chess.hidden or src_chess.color == des_chess.color:
            return False

        src_x, src_y, des_x, des_y = src_chess.x, src_chess.y, des_chess.x, des_chess.y

        # 如果棋子类型是足球，则可以吃任意棋子，但必须间隔至少一个棋子
        if src_chess.type == ChessType.FOOTBALL:
            # 两颗棋子在同一层
            if src_x == des_x:
                can_eat = False
                for i in range(src_y - 1 if src_y > des_y else src_y + 1, des_y, -1 if src_y > des_y else 1):
                    if self.chesses[src_x][i] is not None:
                        can_eat = True
                        break
                return can_eat

            # 两颗棋子不在同一层
            if src_y == des_y:
                can_eat = False
                for i in range(src_x - 1 if src_x > des_x else src_x + 1, des_x, -1 if src_x > des_x else 1):
                    if self.chesses[i][des_y] is not None:
                        can_eat = True
                return can_eat

        if not self.is_next_to(src_x, src_y, des_x, des_y):
            return False

        if src_chess.type == ChessType.MOUSE and des_chess.type == ChessType.ELEPHANT:
            return True

        if src_chess.type == ChessType.ELEPHANT and des_chess.type == ChessType.MOUSE:
            return False

        return src_chess.type >= des_chess.type

    def __change_position(self, src_x, src_y, des_x, des_y):
        self.chesses[des_x][des_y] = self.chesses[src_x][src_y]
        self.chesses[des_x][des_y].x = des_x
        self.chesses[des_x][des_y].y = des_y
        self.chesses[src_x][src_y] = None

    # 翻棋子
    def reverse_chess(self, x, y):
        if not self.is_in_chess_board(x, y):
            raise ChessError("操作的棋子位置不在棋盘内")

        if not self.chesses[x][y].hidden:
            raise ChessError('棋子已经翻开')
        else:
            self.chesses[x][y].hidden = False

    # 吃棋子
    def eat_chess(self, src_x, src_y, des_x, des_y):
        if not self.is_in_chess_board(src_x, src_y) or not self.is_in_chess_board(des_x, des_y):
            raise ChessError('操作的棋子位置不在棋盘内')

        if self.__can_eat(self.chesses[src_x][src_y], self.chesses[des_x][des_y]):
            self.__change_position(src_x, src_y, des_x, des_y)
        else:
            raise ChessError('不能吃掉棋子')

    # 移动棋子
    def move_chess(self, src_x, src_y, des_x, des_y):
        if not self.is_in_chess_board(src_x, src_y) or not self.is_in_chess_board(des_x, des_y):
            raise ChessError('操作的棋子位置不在棋盘内')

        if not self.is_next_to(src_x, src_y, des_x, des_y):
            raise ChessError('只能向相邻位置移动棋子')

        src_chess = self.chesses[src_x][src_y]
        if src_chess is None:
            raise ChessError('棋子不能为空')

        if src_chess.hidden:
            raise ChessError('未翻开的棋子不能移动')

        des_chess = self.chesses[des_x][des_y]
        if des_chess is not None:
            raise ChessError('移动至的位置有棋子，不能移动')

        self.__change_position(src_x, src_y, des_x, des_y)

    # 判断位置坐标是否在棋盘内
    def is_in_chess_board(self, x, y):
        return x in range(0, self.width) and y in range(0, self.length)

    def is_next_to(self, src_x, src_y, des_x, des_y):
        '判断两个位置坐标是否相邻'
        return (src_x == des_x and abs(src_y - des_y) == 1) or (src_y == des_y and abs(src_x - des_x) == 1)
